﻿using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using XFControls.Utils;
using XFControls.XFPanels.XFPanelItems;
using XFControls.XFPanels.XFPanelModals;
using System.Diagnostics;
using System.Collections.Generic;
#pragma warning disable 1591
// ReSharper disable InconsistentNaming

namespace XFControls.XFPanels
{
    public partial class XFPanelBase : Panel
    {
        protected XFPanelAlphabetBar _alphabar;
        protected States _curState;
        protected IXFItem _effect;
        protected bool _hasAnimation;
        protected bool _isAnimating;
        private IXFModal _modal;
        protected Bitmap _offBitmap;
        protected int _offsetHeight;
        protected int _scrollBarX;
        protected XFPanelScrollbar _scrollbar;
        protected bool _scrollbarActive;
        protected Bitmap _slideBuffer;
        private bool _updateSetup;
        private bool _invalidate;
        private bool _invalidateReset;
        private Timer _invalidateTimer;
        private Point _mouseDown;
        protected int notHeight = 50;
        private Point offset;
        private Point scrLast;
        private Point scrVelocity;
        private int slideX;
        private double time;
        private int tolerance = 18;
        protected int visibleHeight;
        private const int _maxVelocity = 5000;
        /// <summary>
        /// Stopwatch for Mouse Move intervals.  Used in calculating momentum
        /// </summary>
        private Stopwatch _lastMove;
        /// <summary>
        /// Stores a sampling of the velocities
        /// </summary>
        private Stack<Point> _velocities;
        private Rectangle _shadowRec;
        private static IImage _shadowData;

        public XFImageCache ImageCache;
        public bool HasBack;
        public XFPanelBase LeftPanel { get; protected set; }
        public XFPanelBase RightPanel { get; protected set; }
        public bool ScrollbarVisible { get; private set; }
        public Color ScrollbarColor { get; set; }
        public bool AlphaBarVisible { get; private set; }
        public Point VirtualLocation { get; protected set; }
        public int LocationX
        {
            get { return Location.X; }
            protected set { Location = new Point(value, 0); }
        }
        public int AlphaBarWidth
        {
            get
            {
                if (_alphabar == null)
                    return 0;
                return _alphabar.Width;
            }
        }
        public States State
        {
            get { return _curState; }
        }
        public PanelAttributes Attributes { get; set; }
        public bool SavePanelAfterSlide { get; set; }
        public string PanelName { get; set; }
        public bool ScrollbarActive
        {
            get { return _scrollbarActive; }
        }

        public event EventHandler<MouseEventArgs> OnScroll;
        public event EventHandler OnFinishSlide;

        public XFPanelBase()
        {
            InitializeComponent();
            customInit();
        }

        public virtual void Reset()
        {
            lock (this)
            {
                if (_offBitmap != null)
                    _offBitmap.Dispose();
                _offBitmap = null;

                if (_effect != null)
                    _effect.Dispose();
                _effect = null;

                if (_alphabar == null) return;
                _alphabar.Location = new Point(Width, 0);
                _alphabar.SendToBack();
            }
        }

        public void SetLeftPanel(XFPanelBase panel)
        {
            LeftPanel = panel;
            LeftPanel.RightPanel = this;
            Parent.Controls.Add(LeftPanel);
        }

        public void SetRightPanel(XFPanelBase panel)
        {
            SetRightPanel(panel, false);
        }

        public void SetRightPanel(XFPanelBase panel, bool forceReplace)
        {
            if (RightPanel != null && Parent != null && Parent.Controls.Contains(RightPanel))
                Parent.Controls.Remove(RightPanel);

            RightPanel = panel;

            if (panel == null)
                return;

            if (forceReplace || RightPanel.LeftPanel == null)
                RightPanel.LeftPanel = this;
            else
            {
                XFPanelBase cur = RightPanel.LeftPanel;
                while (cur.LeftPanel != null)
                {
                    cur = cur.LeftPanel;
                }
                cur.LeftPanel = this;
            }
            if (Parent != null)
                Parent.Controls.Add(RightPanel);
        }

        protected void FinishSlide()
        {
            CleanUp();
            if (LeftPanel != null && !HasBack && !LeftPanel.SavePanelAfterSlide)
            {
                Parent.Controls.Remove(LeftPanel);
                LeftPanel.Dispose();
                LeftPanel = null;
            }
            else if (LeftPanel != null)
            {
                LeftPanel.Deflate();
            }

            if (RightPanel != null && !RightPanel.SavePanelAfterSlide)
            {
                if (Parent == null)
                    RightPanel.Parent.Controls.Add(this);

                if (!RightPanel.IsDisposed)
                {
                    if (Parent != null) Parent.Controls.Remove(RightPanel);
                    RightPanel.Dispose();
                }
                RightPanel = null;
            }
            else if(RightPanel != null)
            {
                RightPanel.Deflate();
            }

            if (HasBack && Parent != null && Parent is XFPanelContainer)
                ((XFPanelContainer)Parent).SetHeaderLeftButton(new XFButtonBack());

            if (!HasBack && Parent != null && Parent is XFPanelContainer)
                ((XFPanelContainer)Parent).SetHeaderLeftButton(null);

            _invalidateTimer.Enabled = true;

            _curState = States.Idle;
            if (Parent != null) ((XFPanelContainer)Parent).SetMainPanel(this);
            Invalidate();

            if (OnFinishSlide != null)
                OnFinishSlide(this, new EventArgs());
        }

        public void EnableSlide()
        {
            if (RightPanel != null)
                _curState = States.SlideLeft;
            else if (LeftPanel != null)
                _curState = States.SlideRight;

            panelSlide();
            Invalidate();
        }

        public void AddEffect(IXFItem effect)
        {
            effect.Parent = this;
            _effect = effect;

            effect.OnAdd();
        }

        public void ClearEffect(IXFItem effect)
        {
            if (_effect != effect) return;
            _effect.Dispose();
            _effect = null;
        }

        public void ClearEffects()
        {
            if (_effect == null) return;
            _effect.Dispose();
            _effect = null;
        }

        public void Damage()
        {
            _invalidate = true;
        }

        public void Damage(bool reset)
        {
            Damage();
            _invalidateReset = reset;
        }

        public virtual void ShowScrollbar(bool show)
        {
            _scrollbarActive = show;
            ScrollbarVisible = false;
        }

        public virtual void ShowAlphaBar(bool show)
        {
            AlphaBarVisible = show;
            if (show)
            {
                _alphabar = new XFPanelAlphabetBar
                            {
                                Height = Parent != null ? Parent.Height : Screen.PrimaryScreen.WorkingArea.Height,
                                XOffset = Width - (ScrollbarVisible ? 5 : 0)
                            };

                _alphabar.Location = new Point(_alphabar.XOffset, 0);
                _alphabar.Parent = this;
            }
            else
                _alphabar = null;
        }

        public virtual void PanelPaint(Graphics g) { }

        public IXFModal ShowModal(XFModal type, string text)
        {
            if (_modal != null)
                _modal.Dispose();

            switch (type)
            {
                case XFModal.Bottom:
                    var modal = new XFModalBottom { Text = text };

                    _modal = modal;
                    break;
                case XFModal.Full:
                    break;
            }

            return _modal;
        }

        public void ShowModal(IXFModal modal)
        {
            if (_modal != null)
                _modal.Dispose();

            _modal = modal;
            modal.Parent = this;

            _curState = States.Modal;
        }

        public virtual void DoSwipe(Point mouseDown, Point mouseUp) { }

        public virtual void ReceiveMouse(Point p, XFPanelMouseEvents e)
        {
            var args = new MouseEventArgs(MouseButtons.Left, 1, p.X, p.Y, 0);
            switch (e)
            {
                case XFPanelMouseEvents.Down:
                    OnMouseDown(args);
                    break;
                case XFPanelMouseEvents.Move:
                    OnMouseMove(args);
                    break;
                case XFPanelMouseEvents.Up:
                    OnMouseUp(args);
                    break;
            }
        }

        //public Bitmap SaveScreen()
        //{
        //    return _offBitmap;
        //}

        //  Events
        protected override void OnPaint(PaintEventArgs e)
        {
            displayScroll();

            onPaintInit(e);

            if (_slideBuffer != null)
            {
                var rec = new Rectangle(slideX, 0, _slideBuffer.Width >> 1, _slideBuffer.Height);
                using (Bitmap tmpSlideBuffer = XFControlUtils.CopyRegion(_slideBuffer, rec))
                {
                    using (Graphics g = Graphics.FromImage(tmpSlideBuffer))
                        drawShadows(g);

                    e.Graphics.DrawImage(tmpSlideBuffer, 0, 0);
                }
                return;
            }
            if (_offBitmap == null) //Bitmap for doublebuffering  
            {
                try
                {
                    _offBitmap = XFBitmapFactory.GetBitmap(Width, visibleHeight);
                }
                catch (OutOfMemoryException) { return; }
            }
            using (Graphics g = Graphics.FromImage(_offBitmap))
            {
                PanelPaint(g);

                if (!(this is XFPanelHeader))
                {
                    if (_alphabar != null)
                        _alphabar.PaintAlphaBar(g);

                    if (_scrollbar != null)
                    {
                        _scrollbar.UpdatePanel(this);
                        _scrollbar.PaintScroll(g);
                    }

                    drawNotifications(g);

                    if (_modal != null)
                        setupModal(g);

                    drawShadows(g);
                }

                drawEffects(g);
            }

            e.Graphics.BitBltImage(_offBitmap, 0);

            if (_curState == States.KineticScroll) scrollKinetic();
        }

        protected void onPaintInit(PaintEventArgs e)
        {
            if (visibleHeight >= e.ClipRectangle.Height) return;
            visibleHeight = e.ClipRectangle.Height;

            if (_offBitmap != null)
                _offBitmap.Dispose();
            _offBitmap = null;
        }

        private void drawShadows(Graphics g)
        {
            try
            {
                if ((Attributes & PanelAttributes.Shadow) != PanelAttributes.Shadow)
                    return;


                if (_shadowRec.Width == 0)
                {
                    SetupShadowData();
                }

                if (_shadowData == null) return;
                IntPtr hdcDest = IntPtr.Zero;

                try
                {
                    var drawRec = new Rectangle(_shadowRec.X, _shadowRec.Y, _shadowRec.X + _shadowRec.Width, _shadowRec.Y + _shadowRec.Height);

                    lock (g)
                    {
                        hdcDest = g.GetHdc();
                        // Ask the Image object from Imaging to draw itself.
                        _shadowData.Draw(hdcDest, ref drawRec, IntPtr.Zero);
                    }
                }
                catch (COMException) { }
                catch (ObjectDisposedException) { }
                catch (ArgumentException) { }
                finally
                {
                    try
                    {
                        if (hdcDest != IntPtr.Zero)
                            g.ReleaseHdc(hdcDest);
                    }
                    catch (Exception) { }
                }
            }
            catch { }
        }

        private void SetupShadowData()
        {
            var temp = new XFItemSimpleText { Parent = this };

            var rec = temp.GetImageRec("header_shadow");

            double ratio = (double)rec.Width / Width;
            int height = (int)(rec.Height / ratio);

            _shadowRec = new Rectangle(0, 0, Width, height);

            if (_shadowData != null) return;

            string uri = "header_shadow";
            byte[] buffer = null;

            if (XFGraphics.ImageCache.ContainsKey(uri))
            {
                var val = XFGraphics.ImageCache[uri];
                if (File.Exists(val))
                {
                    var reader = File.OpenRead(XFGraphics.ImageCache[uri]);
                    buffer = new byte[reader.Length];
                    reader.Read(buffer, 0, buffer.Length);
                }
            }
            if (_shadowData == null)
            {
                var myAssembly = GetType().Assembly;
                string ns = myAssembly.GetName().Name;
                var ms = (MemoryStream)myAssembly.GetManifestResourceStream(ns + ".Resources." + uri + ".png");
                if (ms == null)
                {
                    myAssembly = Assembly.GetExecutingAssembly();
                    ns = myAssembly.GetName().Name;
                    ms = (MemoryStream)myAssembly.GetManifestResourceStream(ns + ".Resources." + uri + ".png");
                    if (ms == null) return;
                }

                buffer = ms.GetBuffer();
            }

            _shadowData = XFGraphics.GetIImage(buffer);
        }

        private void drawEffects(Graphics g)
        {
            if (_effect != null)
            {
                _effect.ItemPaint(g, 0, 0);
            }
        }

        private void drawNotifications(Graphics g)
        {
            if (Parent == null || !(Parent is XFPanelContainer) || ((XFPanelContainer)Parent).UpdateNotification == null) return;
            if (!_updateSetup)
            {
                _updateSetup = true;
                ((XFPanelContainer)Parent).UpdateNotification.Updated += (o, e) =>
                                                                         {
                                                                             if (!IsDisposed)
                                                                                 Damage();
                                                                         };
            }

            IUpdateable notifications = ((XFPanelContainer)Parent).UpdateNotification;

            if (!notifications.Show) return;

            var rec = new Rectangle(0, 0, Width, notHeight);
            using (var b = new SolidBrush(Color.Red))
                g.FillRectangle(b, rec);

            int numb = notifications.GetNumber();
            string display = numb + (numb == 1 ? " New Notification" : " New Notifications");

            using (var b = new SolidBrush(Color.Black))
            using (var f = new Font(FontFamily.GenericSansSerif, 9, FontStyle.Bold))
            {
                Size mes = f.MeasureString(display, new Rectangle(0, 0, Width, 0));
                float y = (notHeight - mes.Height) >> 1;

                g.DrawString(display, f, b, 5, y);

                Rectangle closeRec = XFGraphics.GetImageRec("close");
                closeRec = new Rectangle(Width - closeRec.Width, (notHeight - closeRec.Height) >> 1, closeRec.Width, closeRec.Height);
                g.DrawUIAlphaChannel(this, "close", closeRec);
            }
        }

        private void setupModal(Graphics g)
        {
            switch (_modal.State)
            {
                case XFModalState.Expanding:
                    _modal.CurHeight += (_modal.GetHeight() - _modal.CurHeight) / 3;

                    _hasAnimation = true;
                    if (Math.Abs(_modal.CurHeight - _modal.GetHeight()) <= 3)
                    {
                        _modal.CurHeight = _modal.GetHeight();
                        _modal.State = XFModalState.Open;
                        _hasAnimation = false;
                    }

                    setupAnimation();
                    break;

                case XFModalState.Collapsing:
                    _modal.CurHeight -= _modal.CurHeight / 3;

                    _hasAnimation = true;
                    if (Math.Abs(_modal.CurHeight) <= 3)
                    {
                        _modal.CurHeight = 0;
                        _modal.State = XFModalState.Closed;
                        _modal.Dispose();
                        _modal = null;
                        _hasAnimation = false;
                        return;
                    }

                    setupAnimation();
                    break;

                case XFModalState.Open:
                case XFModalState.Closed:
                    _hasAnimation = false;
                    break;
            }

            if (_modal == null)
                return;

            if (_modal.Buffer == null)
            {
                _modal.Buffer = XFBitmapFactory.GetBitmap(Width, _modal.GetHeight());
                Graphics gm = Graphics.FromImage(_modal.Buffer);
                gm.Clear(BackColor);
                _modal.ItemPaint(gm, 0, 0);
            }
            g.DrawImage(_modal.Buffer, 0, visibleHeight - _modal.CurHeight);
        }

        private void setupAnimation()
        {
            if (_isAnimating) return;

            _isAnimating = true;
            while (_hasAnimation)
            {
                Invalidate();
                Application.DoEvents();
            }
            _isAnimating = false;
        }

        protected override void OnPaintBackground(PaintEventArgs e) { }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            _lastMove.Reset();
            _lastMove.Start();

            switch (_curState)
            {
                case States.Modal:
                    if (isInModal(new Point(e.X, e.Y)))
                    {
                        _mouseDown = new Point(e.X, e.Y);
                        _modal.SendClick(e.X, (e.Y - (visibleHeight - _modal.GetHeight()))); //    convert the point to local coordinate system
                        break;
                    }
                    if (_modal != null && _modal.State == XFModalState.Open)
                        _modal.State = XFModalState.Collapsing;

                    _curState = States.Idle;
                    OnMouseDown(e);
                    break;
                case States.KineticScroll:
                case States.Idle:
                    if (Parent is XFPanelContainer &&
                        ((XFPanelContainer)Parent).UpdateNotification != null &&
                        ((XFPanelContainer)Parent).UpdateNotification.Show &&
                        e.Y <= notHeight)
                    {
                        _curState = States.UpdateClick;
                        return;
                    }

                    if (AlphaPanel(e.X))
                    {
                        _alphabar.PanelMouseDown(e);
                        _curState = States.AlphaPanel;
                        return;
                    }

                    if (_effect != null)
                    {
                        _curState = States.Effect;
                        _effect.OnMouseDown(e.X, e.Y);
                        return;
                    }
                    _curState = States.Click;
                    _mouseDown = new Point(e.X, e.Y);
                    scrVelocity = Point.Empty;
                    scrLast = VirtualLocation;

                    Invalidate();
                    break;
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            switch (_curState)
            {
                case States.Click:
                    if (mouseMoveThreshold(e.Y, false))
                    {
                        _curState = States.Scroll;
                        int offsetY = PointToScreen(new Point(0, e.Y)).Y - VirtualLocation.Y;
                        int offsetX = PointToScreen(new Point(e.X, 0)).X - VirtualLocation.X;

                        offset = new Point(offsetX, offsetY);
                    }
                    else if (mouseMoveThreshold(e.X, true))
                    {
                        _curState = States.Swipe;
                    }
                    break;
                case States.Swipe:
                    if (mouseMoveThreshold(e.Y, false))
                    {
                        _curState = States.Scroll;
                        int offsetY = PointToScreen(new Point(0, e.Y)).Y - VirtualLocation.Y;
                        int offsetX = PointToScreen(new Point(e.X, 0)).X - VirtualLocation.X;

                        offset = new Point(offsetX, offsetY);
                    }
                    break;
                case States.AlphaPanel:
                    if (AlphaPanel(e.X))
                        _alphabar.PanelMouseMove(e);
                    break;
                case States.Scroll:
                    if (_modal != null && _modal.State == XFModalState.Open)
                        _modal.State = XFModalState.Collapsing;

                    int newY = PointToScreen(new Point(0, e.Y)).Y - offset.Y;
                    int newX = PointToScreen(new Point(e.X, 0)).X - offset.X;

                    if (OnScroll != null)
                        OnScroll(this, new MouseEventArgs(MouseButtons.None, 1, newX, newY, 0));

                    ScrollY(newY);
                    ScrollX(newX);

                    if (VirtualLocation != scrLast)
                    {
                        _lastMove.Stop();

                        var tempVelocity = new Point((int)((VirtualLocation.X - scrLast.X) / _lastMove.Elapsed.TotalSeconds),
                            (int)((VirtualLocation.Y - scrLast.Y) / _lastMove.Elapsed.TotalSeconds));

                        _lastMove.Reset();
                        _lastMove.Start();

                        scrLast = VirtualLocation;

                        _velocities.Push(tempVelocity);
                    }
                    break;
                case States.Modal:
                    if (_modal is IXFModalReceiveMouse)
                        ((IXFModalReceiveMouse)_modal).SendMouseMove(e.X, e.Y);

                    if (mouseMoveThreshold(e.X, e.Y) && _modal != null && _modal.State == XFModalState.Open)
                        _modal.ClearClick();
                    break;
                case States.Effect:
                    if (_effect == null)
                    {
                        _curState = States.Idle;
                        return;
                    }
                    _effect.OnMouseMove(e.X, e.Y);
                    break;
            }
            Invalidate();
        }

        private void ScrollY(int newY)
        {
            if (smallPanel() && (Attributes & PanelAttributes.ScrollPast) != PanelAttributes.ScrollPast)
                VirtualLocation = new Point(VirtualLocation.X, 0);
            else if (scrollTooFar(newY, false) && (Attributes & PanelAttributes.ScrollPast) != PanelAttributes.ScrollPast)
                setMainToBottom();
            else if (scrollTooHigh(newY) && (Attributes & PanelAttributes.ScrollPast) != PanelAttributes.ScrollPast)
                VirtualLocation = new Point(VirtualLocation.X, 0);
            else
                VirtualLocation = new Point(VirtualLocation.X, newY);
        }

        private void ScrollX(int newX)
        {
            if (smallPanel() && (Attributes & PanelAttributes.ScrollPast) != PanelAttributes.ScrollPast)
                VirtualLocation = new Point(0, VirtualLocation.Y);
            else if (scrollTooFar(newX, true) && (Attributes & PanelAttributes.ScrollPast) != PanelAttributes.ScrollPast)
                setMainToRight();
            else if (scrollTooHigh(newX) && (Attributes & PanelAttributes.ScrollPast) != PanelAttributes.ScrollPast)
                VirtualLocation = new Point(0, VirtualLocation.Y);
            else
                VirtualLocation = new Point(newX, VirtualLocation.Y);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            switch (_curState)
            {
                case States.UpdateClick:
                    if (e.X > Width - notHeight)
                        ((XFPanelContainer)Parent).UpdateNotification.Show = false;
                    else
                        ((XFPanelContainer)Parent).UpdateNotification.OnClick();

                    if (_curState == States.UpdateClick)
                        _curState = States.Idle;
                    break;
                case States.Modal:
                    switch (_modal.GetClickResult())
                    {
                        case XFModalClickResult.Yes:
                        case XFModalClickResult.No:
                            if (_modal != null && _modal.State == XFModalState.Open)
                                _modal.State = XFModalState.Collapsing;
                            break;
                    }

                    if (_modal is IXFModalReceiveMouse)
                        ((IXFModalReceiveMouse)_modal).SendMouseUp(e.X, e.Y);
                    break;
                case States.Swipe:
                    _curState = States.Idle;
                    DoSwipe(_mouseDown, new Point(e.X, e.Y));
                    if (_curState != States.Idle)
                        OnMouseUp(e);
                    break;
                case States.AlphaPanel:
                    _alphabar.PanelMouseUp(e);
                    _curState = States.Idle;
                    break;
                case States.Effect:
                    _effect.OnMouseUp();
                    _curState = States.Idle;
                    break;
                case States.Scroll:
                    time = 0.0;

                    _curState = States.KineticScroll;
                    SetupScrVelocity();
                    scrollKinetic();
                    //}
                    break;
                case States.SlideLeft:
                    prepSlideLeft();
                    break;
                case States.SlideRight:
                    prepSlideRight();
                    break;
            }

            base.OnMouseUp(e);
            Invalidate();
        }

        private void SetupScrVelocity()
        {
            int count = 0;
            while (_velocities.Count > 0)
            {
                var point = _velocities.Pop();

                if (scrVelocity.IsEmpty)
                {
                    scrVelocity = point;
                    count++;
                    continue;
                }

                if (point.IsEmpty ||
                    (Math.Abs(point.X) > Math.Abs(point.Y) && !SameSign(point.X, scrVelocity.X)) ||
                    (Math.Abs(point.X) < Math.Abs(point.Y) && !SameSign(point.Y, scrVelocity.Y)) ||
                    count > 1)
                {
                    break;
                }

                scrVelocity = new Point(scrVelocity.X + point.X, scrVelocity.Y + point.Y);
                count++;
            }

            if (count > 0) scrVelocity = new Point(scrVelocity.X / count, scrVelocity.Y / count);

            if (Math.Abs(scrVelocity.Y) > _maxVelocity)
                scrVelocity = new Point(scrVelocity.X, scrVelocity.Y > 0 ? _maxVelocity : -_maxVelocity);

            if (Math.Abs(scrVelocity.X) > _maxVelocity)
                scrVelocity = new Point(scrVelocity.X > 0 ? _maxVelocity : -_maxVelocity, scrVelocity.Y);

            _velocities.Clear();
        }

        private bool SameSign(int p, int p_2)
        {
            return (p > 0 && p_2 > 0) || (p < 0 && p_2 < 0);
        }

        protected void ClearOnScroll()
        {
            OnScroll = null;
        }

        protected void prepSlideLeft()
        {
            if (RightPanel == null)
                return;

            RightPanel._curState = States.Busy;

            if (Height < Screen.PrimaryScreen.WorkingArea.Height)
                Height = Screen.PrimaryScreen.WorkingArea.Height;
            panelSlide();
        }

        protected void prepSlideRight()
        {
            LeftPanel._curState = States.Busy;

            if (Height < Screen.PrimaryScreen.WorkingArea.Height)
                Height = Screen.PrimaryScreen.WorkingArea.Height;
            panelSlide();
        }

        public void ShowTextInput(TextBox TextBox)
        {
            Controls.Add(TextBox);
            if (Parent != null && Parent is XFPanelContainer)
                ((XFPanelContainer)Parent).ShowTextBox();
        }

        public void HideTextInput(TextBox TextBox)
        {
            Controls.Remove(TextBox);
            if (Parent != null && Parent is XFPanelContainer)
                ((XFPanelContainer)Parent).HideTextBox();
        }

        //  Queries
        protected void displayScroll()
        {
            int height = Parent != null ? Parent.Height : Screen.PrimaryScreen.WorkingArea.Height;
            if ((_scrollbarActive && ScrollbarVisible && Height > height) || (!_scrollbarActive && !ScrollbarVisible))
                return;

            ScrollbarVisible = _scrollbarActive && Height > height;

            if (ScrollbarVisible)
            {
                _scrollbar = new XFPanelScrollbar
                             {
                                 Width = 5,
                                 Height = height,
                                 xOffset = _scrollBarX > 0 ? _scrollBarX : Width,
                                 BarColor = ScrollbarColor
                             };

                Reset();

                if (_alphabar != null)
                {
                    _alphabar.XOffset = Width - 5;
                }
            }
            else if (_scrollbar != null)
            {
                _scrollbar.Dispose();
                _scrollbar = null;
            }
        }

        private bool AlphaPanel(int x)
        {
            return AlphaBarVisible && x > (_alphabar.XOffset - _alphabar.Width);
        }

        private bool mouseMoveThreshold(int x, int y)
        {
            double dx = _mouseDown.X - x;
            double dy = _mouseDown.Y - y;

            dx *= dx;
            dy *= dy;

            double total = dx + dy;
            total = Math.Sqrt(total);

            return total > tolerance;
        }

        private bool mouseMoveThreshold(int val, bool isX)
        {
            double dv = isX ? Math.Abs(_mouseDown.X - val) : Math.Abs(_mouseDown.Y - val);

            if (_curState != States.Swipe)
                return dv > tolerance;
            return dv > (Screen.PrimaryScreen.WorkingArea.Height >> 3);
        }

        private bool isInModal(Point point)
        {
            if (_modal == null || _modal.State != XFModalState.Open)
                return false;

            if (point.Y > visibleHeight - _modal.GetHeight())
                return true;
            return false;
        }

        private void customInit()
        {
            Attributes |= PanelAttributes.Shadow;
            visibleHeight = 0;
            LocationX = 0;
            VirtualLocation = new Point(Location.X, Location.Y);
            _curState = States.Idle;
            SavePanelAfterSlide = false;
            _hasAnimation = false;
            _scrollBarX = -1;
            _offsetHeight = -1;
            ImageCache = new XFImageCache();

            if (XFGraphics.GetScreenResolutionNotRotated() == ScreenResolution.QVGA)
            {
                notHeight /= 2;
                tolerance /= 2;
            }

            _invalidateTimer = new Timer
                               {
                                   Interval = 100
                               };

            _invalidateTimer.Tick += (o, e) =>
                                     {
                                         if (_invalidate)
                                         {
                                             _invalidate = false;
                                             Invalidate();
                                         }
                                         if (!_invalidateReset) return;
                                         _invalidateReset = false;
                                         Reset();
                                     };
            _invalidateTimer.Enabled = true;

            _lastMove = new Stopwatch();
            _velocities = new Stack<Point>();
        }

        private bool smallPanel()
        {
            if (Parent == null)
                return true;
            return Height < Parent.Height;
        }

        protected static bool scrollTooHigh(int location)
        {
            return location > 0;
        }

        protected bool scrollTooFar(int y, bool x)
        {
            if (Parent == null)
                return true;
            if (x)
                return (Width + y) < Parent.Width;
            return (Height + y) < (_offsetHeight > 0 ? _offsetHeight : Parent.Height);
        }

        private void setMainToBottom()
        {
            int offsetHeight = (_offsetHeight > 0 ? _offsetHeight : Parent.Height);
            int y = Height - offsetHeight;
            int offsetScroll = VirtualLocation.Y + y;

            if ((Attributes & PanelAttributes.ScrollPast) == PanelAttributes.ScrollPast)
            {
                VirtualLocation = new Point(VirtualLocation.X, VirtualLocation.Y - (offsetScroll >> 1));
            }
            scrVelocity = new Point(scrVelocity.X, 0);

            if (offsetScroll > -2)
                VirtualLocation = new Point(VirtualLocation.X, -y);
        }

        private void setMainToRight()
        {
            int offsetWidth = Parent.Width;
            int x = Width - offsetWidth;
            int offsetScroll = VirtualLocation.X + x;

            if ((Attributes & PanelAttributes.ScrollPast) == PanelAttributes.ScrollPast)
            {
                VirtualLocation = new Point(VirtualLocation.X - (offsetScroll >> 1), VirtualLocation.Y);
            }
            scrVelocity = new Point(0, scrVelocity.Y);

            if (offsetScroll > -2)
                VirtualLocation = new Point(-x, VirtualLocation.Y);
        }

        private void scrollKinetic()
        {
            if (IsDisposed)
            {
                return;
            }

            _lastMove.Stop();

            //var distY = GetDistance((int)(scrVelocity.Y * _lastMove.Elapsed.TotalSeconds));
            //var distX = GetDistance((int)(scrVelocity.X * _lastMove.Elapsed.TotalSeconds));
            var distY = GetDistance((int)(scrVelocity.Y * 0.015));
            var distX = GetDistance((int)(scrVelocity.X * 0.015));

            _lastMove.Reset();
            _lastMove.Start();

            if (distY + distX <= 0 && !scrollTooHigh(VirtualLocation.X) && !scrollTooHigh(VirtualLocation.Y))
            {
                _curState = States.Idle;
                Invalidate();
                return;
            }

            CalculatePosition(distY, scrVelocity.Y, false);
            CalculatePosition(distX, scrVelocity.X, true);

            Invalidate();
            const double elaps = .3;
            time += elaps;
            //Application.DoEvents();
        }

        private void CalculatePosition(double dist, int velocity, bool x)
        {
            int newLocation;

            if (velocity > 0)
            {
                newLocation = x ? (int)(VirtualLocation.X + dist) : (int)(VirtualLocation.Y + dist);
            }
            else
            {
                newLocation = x ? (int)(VirtualLocation.X - dist) : (int)(VirtualLocation.Y - dist);
            }

            if (scrollTooHigh(newLocation))
            {
                VirtualLocation = x ? new Point(newLocation, VirtualLocation.Y)
                                    : new Point(VirtualLocation.X, newLocation);

                if ((Attributes & PanelAttributes.ScrollPast) == PanelAttributes.ScrollPast)
                {
                    VirtualLocation = x ? new Point(VirtualLocation.X >> 1, VirtualLocation.Y)
                                        : new Point(VirtualLocation.X, VirtualLocation.Y >> 1);
                }
                else
                {
                    VirtualLocation = x ? new Point(0, VirtualLocation.Y)
                                        : new Point(VirtualLocation.X, 0);
                }

                if ((x && VirtualLocation.X <= 2) || (!x && VirtualLocation.Y <= 2))
                    VirtualLocation = x ? new Point(0, VirtualLocation.Y) : new Point(VirtualLocation.X, 0);
                scrVelocity = x ? new Point(0, scrVelocity.Y) : new Point(scrVelocity.X, 0);
                return;
            }

            if (scrollTooFar(newLocation, x))
            {
                if (x)
                    setMainToRight();
                else
                    setMainToBottom();

                Invalidate();
                return;
            }

            VirtualLocation = x ? new Point(newLocation, VirtualLocation.Y) : new Point(VirtualLocation.X, newLocation);
        }

        private double GetDistance(int velocity)
        {
            double falloff = 3.0;

            int perc;

            if ((perc = Height / 1000) > 0)
            {
                if (perc > 50)
                    falloff = 2.0;
                else
                    falloff = falloff - (falloff * ((double)perc / 100));
            }

            double ret = Math.Abs(velocity) - (falloff * time);
            return ret > 0 ? ret : 0;
        }

        private void panelSlide()
        {
            try
            {
                if (_slideBuffer == null)
                    setupSlideBuffer();
            }
            catch (OutOfMemoryException)
            {
                InstaSlide();
                return;
            }

            while (true)
            {
                switch (_curState)
                {
                    case States.SlideLeft:
                        int move = (Width + slideX) / 3;
                        if (RightPanel == null)
                        {
                            _curState = States.Idle;

                            if (_slideBuffer != null)
                                _slideBuffer.Dispose();
                            _slideBuffer = null;
                            return;
                        }

                        slideX -= move;

                        if (2 > move)
                        {
                            _curState = States.Idle;
                            RightPanel.LocationX = 0;
                            LocationX = Width;
                            RightPanel.FinishSlide();

                            if (_slideBuffer != null)
                                _slideBuffer.Dispose();
                            _slideBuffer = null;
                            return;
                        }
                        break;
                    case States.SlideRight:
                        move = Math.Abs(slideX) / 3;
                        if (LeftPanel == null)
                        {
                            _curState = States.Idle;

                            if (_slideBuffer != null)
                                _slideBuffer.Dispose();
                            _slideBuffer = null;
                            return;
                        }

                        slideX += move;

                        if (2 > move)
                        {
                            _curState = States.Idle;
                            LeftPanel.LocationX = 0;
                            LocationX = Width;
                            LeftPanel.FinishSlide();

                            if (_slideBuffer != null)
                                _slideBuffer.Dispose();
                            _slideBuffer = null;
                            return;
                        }
                        break;
                    case States.Idle:
                        return;
                }
                Invalidate();
                Application.DoEvents();
            }
        }

        private void InstaSlide()
        {
            CleanUp();
            switch (_curState)
            {
                case States.SlideLeft:
                    _curState = States.Idle;
                    RightPanel.LocationX = 0;
                    LocationX = Width;
                    RightPanel.FinishSlide();

                    if (_slideBuffer != null)
                        _slideBuffer.Dispose();
                    _slideBuffer = null;
                    return;
                case States.SlideRight:
                    _curState = States.Idle;
                    LeftPanel.LocationX = 0;
                    LocationX = Width;
                    LeftPanel.FinishSlide();

                    if (_slideBuffer != null)
                        _slideBuffer.Dispose();
                    _slideBuffer = null;
                    return;
                case States.Idle:
                    return;
            }
        }

        private void setupSlideBuffer()
        {
            _slideBuffer = XFBitmapFactory.GetBitmap(Width * 2, Screen.PrimaryScreen.WorkingArea.Height);

            switch (_curState)
            {
                case States.SlideLeft:
                    if (RightPanel == null)
                        return;

                    using (var buffer = XFBitmapFactory.GetBitmap(Width, Screen.PrimaryScreen.WorkingArea.Height))
                    using (Graphics gb = Graphics.FromImage(buffer))
                    using (Graphics g = Graphics.FromImage(_slideBuffer))
                    {
                        g.Clear(BackColor);
                        gb.Clear(BackColor);

                        PanelPaint(g);
                        RightPanel.PanelPaint(gb);

                        g.DrawImage(buffer, Width, 0);
                    }

                    slideX = 0;
                    break;
                case States.SlideRight:
                    if (LeftPanel == null)
                        return;

                    using (var buffer = XFBitmapFactory.GetBitmap(Width, Screen.PrimaryScreen.WorkingArea.Height))
                    using (Graphics gb = Graphics.FromImage(buffer))
                    using (Graphics g = Graphics.FromImage(_slideBuffer))
                    {
                        g.Clear(BackColor);
                        gb.Clear(BackColor);

                        PanelPaint(gb);
                        LeftPanel.PanelPaint(g);

                        g.DrawImage(buffer, Width, 0);
                    }

                    slideX = -Width;
                    break;
            }
        }

        public virtual void CleanUp()
        {
            ImageCache.Dispose();
            ImageCache = null;
            ImageCache = new XFImageCache();
        }

        protected virtual void Deflate()
        {
            CleanUp();

            _invalidateTimer.Enabled = false;

            if (_offBitmap != null)
                _offBitmap.Dispose();

            _offBitmap = null;
        }

        protected new virtual void Dispose()
        {
            _invalidateTimer.Enabled = false;
            base.Dispose();
            ImageCache.Dispose();
            ImageCache = null;

            if (_offBitmap != null)
                _offBitmap.Dispose();

            if (_slideBuffer != null)
                _slideBuffer.Dispose();

            if (_alphabar != null)
                _alphabar.Dispose();
            if (_scrollbar != null)
                _scrollbar.Dispose();
            if (_modal != null)
                _modal.Dispose();

            OnScroll = null;
            OnFinishSlide = null;

            _invalidateTimer.Dispose();
        }
    }


    [Flags]
    public enum PanelAttributes
    {
        None = 0x0,
        ScrollPast = 0x1,
        Swipe = 0x2,
        Shadow = 0x4
    }

    public enum States
    {
        Idle,
        Busy,
        Click,
        AlphaPanel,
        UpdateClick,
        Scroll,
        Swipe,
        KineticScroll,
        SlideLeft,
        SlideRight,
        Modal,
        Effect
    }

    public enum XFModal
    {
        Full,
        Bottom
    }

    public class TextInputEventArgs : EventArgs
    {
        public TextInputEventArgs(string text)
        {
            TextEntered = text;
        }

        public string TextEntered { get; set; }
    }

    public enum XFPanelMouseEvents
    {
        Down,
        Move,
        Up
    }
}

// ReSharper restore InconsistentNaming
#pragma warning restore 1591
